package impl

import (
	"context"
	"net/http"

	"gitee.com/go-course/go11/vblog/apps/blog"
	"gitee.com/go-course/go11/vblog/common"
)

// ctx: Context上下文, 一个接口或者函数, 他的参数保护2类数据:
// 1. 用户传递过来的数据: in *blog.Blog
// 2. 还有1类数据 不是通过用户传递的, 比如Trace, RequestId
func (i *impl) CreateBlog(ctx context.Context, in *blog.CreateBlogRequest) (
	*blog.Blog, error) {

	// 检查用户的参数
	if err := in.Validate(); err != nil {
		// 我这里就想返回一个业务异常?
		// 当前Error里面不保护异常码信息?
		return nil, common.NewApiException(http.StatusBadRequest, err.Error())
	}

	// 生成一个blog对象
	// ins := blog.Blog{
	// 	Meta:              &blog.Meta{},
	// 	CreateBlogRequest: &blog.CreateBlogRequest{},
	// }
	// ins.CreateBlogRequest.Extra 这个map 就没有初始化, 很容易报空指针错误
	// 代码不具有可兼容

	// 使用构造函数来创建一个对象
	ins := blog.NewBlog(in)

	// 直接和数据库交互
	// 尽量不要直接这样写？ 比如用户提交一个请求
	// INSERT INTO `blogs` (`created_at`,`updated_at`,`published_at`,`title`,`author`,`content`,`tags`,`status`)
	// VALUES (1684566571,1684566571,0,'test','test','test','{}','0')
	err := i.db.WithContext(ctx).Save(ins).Error
	if err != nil {
		return nil, common.NewApiException(http.StatusInternalServerError, err.Error())
	}

	//
	// go func() {
	// 	<-ctx.Done()
	// 	// 退出
	// 	// 取消该次数据库操作
	// }()

	return ins, nil
}

// 查询文章列表
func (i *impl) QueryBlog(ctx context.Context, in *blog.QueryBlogRequest) (
	*blog.BlogSet, error) {
	// 提前构造一个BlogSet对象出来
	set := blog.NewBlogSet()

	//  需要构造SQL查询
	query := i.db.WithContext(ctx).Model(&blog.Blog{})

	// 模糊查询
	if in.Keywords != "" {
		// %xxxx% ---> *xxxx*
		// go项目教学  --> 项目教学     (%项目教学%)
		query = query.Where("content LIKE ?", "%"+in.Keywords+"%")
	}
	if in.Author != "" {
		query = query.Where("author = ?", in.Author)
	}

	// 分页是必传参数? offset 忽略前面多少个，获取多少个数据
	// 数据是一个[]*Blog
	err := query.
		// COUNT(*) WHERE xxx OFFSET LIMIT
		Count(&set.Total).
		Offset(int(in.Offset())).
		Limit(in.PageSize).
		// SELECT * FORM ....
		// SELECT * FROM `blogs` LIMIT 20
		// SELECT * FROM `blogs` WHERE content LIKE '%项目教学%' LIMIT 20
		// MySQL的教程对SQL基本使用有所了解
		Find(&set.Items).
		Error
	if err != nil {
		return nil, err
	}

	return set, nil
}

// 查询单个文章
// id = "xxx" 根据文章id的一个条件过滤查询
func (i *impl) DescribeBlog(ctx context.Context, in *blog.DescribeBlogRequest) (
	*blog.Blog, error) {
	//  需要构造SQL查询
	query := i.db.WithContext(ctx).Model(&blog.Blog{})

	ins := blog.NewBlog(blog.NewCreateBlogRequest())
	err := query.
		Where("id = ?", in.Id).
		First(ins).
		Error
	if err != nil {
		return nil, err
	}

	return ins, nil
}

// 更新文章
func (i *impl) UpdateBlog(ctx context.Context, in *blog.UpdateBlogRequest) (
	*blog.Blog, error) {
	if err := in.Validate(); err != nil {
		return nil, err
	}

	err := i.GetDB(ctx).Where("id = ?", in.Id).Updates(in.CreateBlogRequest).Error
	if err != nil {
		return nil, err
	}

	// 只是为了验证这个对象是否存在, 查询更新的实例, 方便前端直接进行对象的展示
	ins, err := i.DescribeBlog(ctx, in.DescribeBlogRequest)
	if err != nil {
		return nil, err
	}

	return ins, nil
}

// 删除文章, 返回删除的对象, 用前端提升, 用于对象最终
func (i *impl) DeleteBlog(ctx context.Context, in *blog.DeleteBlogRequest) (
	*blog.Blog, error) {

	// 只是为了验证这个对象是否存在, 查询更新的实例, 方便前端直接进行对象的展示
	ins, err := i.DescribeBlog(ctx, in.DescribeBlogRequest)
	if err != nil {
		return nil, err
	}

	err = i.GetDB(ctx).Where("id = ?", in.Id).Delete(ins).Error
	if err != nil {
		return nil, err
	}
	return ins, nil
}
